"""
Collision detection system for tank battle game
"""
import pygame
from typing import List, Tuple, Optional
from .tank import Tank
from .bullet import Bullet
from .map import GameMap, MapTile
from utils.constants import CollisionType, TileType, PowerUpType
from .power_up import get_power_up_manager


class CollisionDetector:
    """Collision detection system"""
    
    def __init__(self, game_map: GameMap):
        """
        Initialize collision detector
        
        Args:
            game_map: Game map instance
        """
        self.game_map = game_map
    
    def check_tank_map_collision(self, tank: Tank) -> bool:
        """
        Check collision between tank and map
        
        Args:
            tank: Tank to check
            
        Returns:
            True if collision detected
        """
        tank_rect = tank.get_collision_rect()
        return self.game_map.check_collision(tank_rect)
    
    def check_bullet_map_collision(self, bullet: Bullet) -> Tuple[bool, List[MapTile]]:
        """
        Check collision between bullet and map
        
        Args:
            bullet: Bullet to check
            
        Returns:
            Tuple of (collision_detected, destroyed_tiles)
        """
        bullet_rect = bullet.get_collision_rect()
        tiles = self.game_map.get_tiles_in_rect(bullet_rect)
        
        # Check collision with solid tiles
        collision_detected = False
        solid_tiles = []
        
        for tile in tiles:
            if tile.solid:
                # 如果是钢墙，检查玩家是否有钢墙破坏道具
                if tile.tile_type == TileType.STEEL:
                    power_up_manager = get_power_up_manager()
                    if bullet.get_owner() == "player" and power_up_manager.has_power_up(PowerUpType.STEEL_BREAKER):
                        # 玩家有钢墙破坏道具，钢墙可被破坏
                        solid_tiles.append(tile)
                        collision_detected = True
                    else:
                        # 钢墙不可被破坏
                        solid_tiles.append(tile)
                        collision_detected = True
                else:
                    # 其他固体瓦片
                    solid_tiles.append(tile)
                    collision_detected = True
        
        # Damage destructible tiles
        destroyed_tiles = []
        if collision_detected:
            # 检查玩家是否有钢墙破坏道具
            can_destroy_steel = False
            if bullet.get_owner() == "player":
                power_up_manager = get_power_up_manager()
                can_destroy_steel = power_up_manager.has_power_up(PowerUpType.STEEL_BREAKER)
            
            destroyed_tiles = self.game_map.damage_tiles_in_rect(bullet_rect, bullet.get_damage(), can_destroy_steel)
            
            # Add visual effects for destroyed tiles
            for tile in destroyed_tiles:
                self._create_destruction_effect(tile)
        
        return collision_detected, destroyed_tiles
    
    def _create_destruction_effect(self, tile: MapTile):
        """
        Create visual effect when tile is destroyed
        
        Args:
            tile: Destroyed tile
        """
        # This could be expanded to create particle effects
        # For now, we'll just mark the tile as destroyed
        pass
    
    def check_tank_tank_collision(self, tank1: Tank, tank2: Tank) -> bool:
        """
        Check collision between two tanks
        
        Args:
            tank1: First tank
            tank2: Second tank
            
        Returns:
            True if collision detected
        """
        if not tank1.is_alive() or not tank2.is_alive():
            return False
        
        rect1 = tank1.get_collision_rect()
        rect2 = tank2.get_collision_rect()
        return rect1.colliderect(rect2)
    
    def check_bullet_tank_collision(self, bullet: Bullet, tank: Tank) -> bool:
        """
        Check collision between bullet and tank
        
        Args:
            bullet: Bullet to check
            tank: Tank to check
            
        Returns:
            True if collision detected
        """
        if not bullet.is_active() or not tank.is_alive():
            return False
        
        # Don't hit owner
        if bullet.get_owner() == tank.tank_type:
            return False
        
        bullet_rect = bullet.get_collision_rect()
        tank_rect = tank.get_collision_rect()
        return bullet_rect.colliderect(tank_rect)
    
    def check_bullet_bullet_collision(self, bullet1: Bullet, bullet2: Bullet) -> bool:
        """
        Check collision between two bullets
        
        Args:
            bullet1: First bullet
            bullet2: Second bullet
            
        Returns:
            True if collision detected
        """
        if not bullet1.is_active() or not bullet2.is_active():
            return False
        
        rect1 = bullet1.get_collision_rect()
        rect2 = bullet2.get_collision_rect()
        return rect1.colliderect(rect2)
    
    def check_boundary_collision(self, rect: pygame.Rect) -> bool:
        """
        Check collision with screen boundaries
        
        Args:
            rect: Rectangle to check
            
        Returns:
            True if collision detected
        """
        return not self.game_map.boundary_rect.contains(rect)
    
    def get_collision_response(self, moving_rect: pygame.Rect, 
                              static_rect: pygame.Rect) -> Tuple[int, int]:
        """
        Calculate collision response for moving object
        
        Args:
            moving_rect: Moving object rectangle
            static_rect: Static object rectangle
            
        Returns:
            Tuple of (dx, dy) adjustment values
        """
        if not moving_rect.colliderect(static_rect):
            return 0, 0
        
        # Calculate overlap
        overlap_left = moving_rect.right - static_rect.left
        overlap_right = static_rect.right - moving_rect.left
        overlap_top = moving_rect.bottom - static_rect.top
        overlap_bottom = static_rect.bottom - moving_rect.top
        
        # Find minimum overlap
        min_overlap = min(overlap_left, overlap_right, overlap_top, overlap_bottom)
        
        # Adjust position based on minimum overlap
        if min_overlap == overlap_left:
            return -overlap_left, 0
        elif min_overlap == overlap_right:
            return overlap_right, 0
        elif min_overlap == overlap_top:
            return 0, -overlap_top
        elif min_overlap == overlap_bottom:
            return 0, overlap_bottom
        
        return 0, 0
    
    def resolve_tank_map_collision(self, tank: Tank) -> bool:
        """
        Resolve collision between tank and map
        
        Args:
            tank: Tank to resolve collision for
            
        Returns:
            True if collision was resolved
        """
        tank_rect = tank.get_collision_rect()
        
        # First, enforce strict boundary limits
        boundary_violated = False
        original_x, original_y = tank.x, tank.y
        
        # Clamp tank position to boundaries
        if tank_rect.left < self.game_map.boundary_rect.left:
            tank.x = self.game_map.boundary_rect.left
            boundary_violated = True
        elif tank_rect.right > self.game_map.boundary_rect.right:
            tank.x = self.game_map.boundary_rect.right - tank.width
            boundary_violated = True
            
        if tank_rect.top < self.game_map.boundary_rect.top:
            tank.y = self.game_map.boundary_rect.top
            boundary_violated = True
        elif tank_rect.bottom > self.game_map.boundary_rect.bottom:
            tank.y = self.game_map.boundary_rect.bottom - tank.height
            boundary_violated = True
        
        # Update rect if boundary was violated
        if boundary_violated:
            tank_rect = tank.get_collision_rect()
        
        # Then check map tile collisions
        if not self.check_tank_map_collision(tank):
            return boundary_violated
        
        # Get tiles that tank is colliding with
        tiles = self.game_map.get_tiles_in_rect(tank_rect)
        
        # Try to resolve collision by moving tank back
        
        # Strategy 1: Move back in opposite direction of movement
        if tank.moving:
            dx, dy = tank._get_direction_vector(tank.direction)
            # Move back by speed amount
            tank.set_position(tank.x - dx * tank.speed, tank.y - dy * tank.speed)
            if not self.check_tank_map_collision(tank):
                return True
            else:
                # Restore position if still colliding
                tank.set_position(original_x, original_y)
        
        # Strategy 2: Find minimum overlap resolution
        best_dx, best_dy = 0, 0
        min_distance = float('inf')
        
        for tile in tiles:
            if tile.solid:
                dx, dy = self.get_collision_response(tank_rect, tile.rect)
                distance = abs(dx) + abs(dy)
                
                if distance < min_distance:
                    min_distance = distance
                    best_dx, best_dy = dx, dy
        
        # Apply resolution
        if best_dx != 0 or best_dy != 0:
            new_x = tank.x + best_dx
            new_y = tank.y + best_dy
            
            # Ensure resolution doesn't push tank out of bounds
            new_x = max(0, min(new_x, self.game_map.width - tank.width))
            new_y = max(0, min(new_y, self.game_map.height - tank.height))
            
            tank.set_position(new_x, new_y)
            return True
        
        # Strategy 3: Try to slide along walls
        return self._try_slide_resolution(tank, tiles)
    
    def _try_slide_resolution(self, tank: Tank, tiles: List[MapTile]) -> bool:
        """
        Try to resolve collision by sliding along walls
        
        Args:
            tank: Tank to resolve collision for
            tiles: Colliding tiles
            
        Returns:
            True if sliding resolution was successful
        """
        tank_rect = tank.get_collision_rect()
        original_x, original_y = tank.x, tank.y
        
        # Try sliding in X direction only
        tank.set_position(tank.x, original_y)
        if not self.check_tank_map_collision(tank):
            return True
        
        # Try sliding in Y direction only
        tank.set_position(original_x, tank.y)
        if not self.check_tank_map_collision(tank):
            return True
        
        # Restore original position
        tank.set_position(original_x, original_y)
        return False
    
    def check_all_collisions(self, tanks: List[Tank], bullets: List[Bullet]) -> dict:
        """
        Check all collisions in the game
        
        Args:
            tanks: List of tanks
            bullets: List of bullets
            
        Returns:
            Dictionary of collision results
        """
        results = {
            'tank_map_collisions': [],
            'bullet_map_collisions': [],
            'tank_tank_collisions': [],
            'bullet_tank_collisions': [],
            'bullet_bullet_collisions': [],
            'boundary_collisions': [],
            'collision_events': []  # New: track collision events for effects
        }
        
        # Check tank-map collisions
        for tank in tanks:
            if tank.is_alive() and self.check_tank_map_collision(tank):
                results['tank_map_collisions'].append(tank)
                results['collision_events'].append({
                    'type': 'tank_wall',
                    'tank': tank,
                    'position': (tank.x, tank.y)
                })
        
        # Check bullet-map collisions
        for bullet in bullets:
            if bullet.is_active():
                collision, destroyed_tiles = self.check_bullet_map_collision(bullet)
                if collision:
                    results['bullet_map_collisions'].append({
                        'bullet': bullet,
                        'destroyed_tiles': destroyed_tiles
                    })
                    results['collision_events'].append({
                        'type': 'bullet_wall',
                        'bullet': bullet,
                        'position': (bullet.x, bullet.y),
                        'destroyed_tiles': destroyed_tiles
                    })
        
        # Check tank-tank collisions
        for i, tank1 in enumerate(tanks):
            for tank2 in tanks[i+1:]:
                if self.check_tank_tank_collision(tank1, tank2):
                    results['tank_tank_collisions'].append((tank1, tank2))
                    results['collision_events'].append({
                        'type': 'tank_tank',
                        'tank1': tank1,
                        'tank2': tank2,
                        'position': ((tank1.x + tank2.x) // 2, (tank1.y + tank2.y) // 2)
                    })
        
        # Check bullet-tank collisions
        for bullet in bullets:
            for tank in tanks:
                if self.check_bullet_tank_collision(bullet, tank):
                    results['bullet_tank_collisions'].append({
                        'bullet': bullet,
                        'tank': tank,
                        'damage': bullet.get_damage()
                    })
                    results['collision_events'].append({
                        'type': 'bullet_tank',
                        'bullet': bullet,
                        'tank': tank,
                        'damage': bullet.get_damage(),
                        'position': (bullet.x, bullet.y)
                    })
        
        # Check bullet-bullet collisions
        for i, bullet1 in enumerate(bullets):
            for bullet2 in bullets[i+1:]:
                if self.check_bullet_bullet_collision(bullet1, bullet2):
                    results['bullet_bullet_collisions'].append((bullet1, bullet2))
                    results['collision_events'].append({
                        'type': 'bullet_bullet',
                        'bullet1': bullet1,
                        'bullet2': bullet2,
                        'position': ((bullet1.x + bullet2.x) // 2, (bullet1.y + bullet2.y) // 2)
                    })
        
        # Check boundary collisions
        for tank in tanks:
            if tank.is_alive():
                tank_rect = tank.get_collision_rect()
                if self.check_boundary_collision(tank_rect):
                    results['boundary_collisions'].append(tank)
                    results['collision_events'].append({
                        'type': 'tank_boundary',
                        'tank': tank,
                        'position': (tank.x, tank.y)
                    })
        
        for bullet in bullets:
            if bullet.is_active():
                bullet_rect = bullet.get_collision_rect()
                if self.check_boundary_collision(bullet_rect):
                    results['boundary_collisions'].append(bullet)
                    results['collision_events'].append({
                        'type': 'bullet_boundary',
                        'bullet': bullet,
                        'position': (bullet.x, bullet.y)
                    })
        
        return results
    
    def handle_collision_events(self, collision_results: dict) -> List[dict]:
        """
        Handle collision events and return effects to be created
        
        Args:
            collision_results: Results from check_all_collisions
            
        Returns:
            List of effects to be created
        """
        effects = []
        
        for event in collision_results.get('collision_events', []):
            if event['type'] == 'bullet_tank':
                # Create hit effect
                effects.append({
                    'type': 'hit_effect',
                    'position': event['position'],
                    'damage': event['damage']
                })
            
            elif event['type'] == 'bullet_wall':
                # Create wall hit effect
                effects.append({
                    'type': 'wall_hit_effect',
                    'position': event['position'],
                    'destroyed_tiles': event.get('destroyed_tiles', [])
                })
            
            elif event['type'] == 'bullet_bullet':
                # Create bullet collision effect
                effects.append({
                    'type': 'bullet_collision_effect',
                    'position': event['position']
                })
        
        return effects